package speed.mvc.servlet;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.velocity.Template;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.context.Context;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.servlet.VelocityServlet;

import speed.context.SpeedApplicationContext;
import speed.context.SpeedApplicationModule;
import speed.context.SpeedContext;
import speed.mvc.action.BaseAction;
import speed.mvc.annotations.Path;
import speed.mvc.scanner.SpeedScanner;

import com.google.inject.Module;

/**
 * speedframework中MVC部分最核心的类
 * 
 * @author zhaohaolin(zhaohaolin2010@gmail.com)
 */
@SuppressWarnings("deprecation")
public class SpeedController extends VelocityServlet {

	// 对请求处理的核心方法
	public Template handleRequest(HttpServletRequest request,
			HttpServletResponse response, Context vctx) {
		long start = System.currentTimeMillis();
		Template template = null;
		try {
			// 1.对请求进来的URL进行分解,以取得较短的请求字符串
			String className = "";
			String methodName = "index";
			String url = "";
			String path = request.getContextPath();
			String temp = request.getRequestURI();
			url = temp;
			if (!StringUtils.isEmpty(path)) {
				url = url.replace(path, "");
			}
			url = url.substring(1, url.indexOf('.' + ACTION_SUFFIX));// 从第一个"/"到后缀处截断
			String[] tempUrls = url.split("\\/");
			className = tempUrls[0];
			if (tempUrls.length >= 2 && !StringUtils.isEmpty(tempUrls[1])) {
				methodName = tempUrls[1];
			}
			// 根据请求的第一级匹配类
			ActionBean bean = BeanFactoryHashMap.get(className);
			BaseAction handler = bean.getAction();
			logger.info("Class Intanceof " + handler.toString());
			// 根据第二级匹配方法
			Method method = bean.getMethodMap().get(methodName);
			// 2.根据请求的URI到内存缓存中查询出对应的Action和Method出来
			// 执行相应的Action后，返回的Model值放到context中去
			handler.setRequest(request);
			handler.setResponse(response);
			handler.setVcontext(vctx);
			handler.setSpeedContext(context);
			String viewName = (String) method.invoke(handler, new Object[] {});
			// 检查返回的视图名是否包含前缀"redirect:",如果有，则重定向，否则直接返回视图模板去解析
			if ((viewName).startsWith("redirect:")) {
				try {
					response.sendRedirect(viewName.substring(
							"redirect:".length(), viewName.length()));
					return null;
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			try {
				template = getTemplate(viewName + ".vm");
				long end = System.currentTimeMillis();
				logger.info("invoke method:" + methodName + ",view:" + viewName
						+ " use times [" + (end - start) + "] ms");
			} catch (ParseErrorException pee) {
				System.out.println("parse error for template " + pee);
			} catch (ResourceNotFoundException rnfe) {
				System.out.println("template not found " + rnfe);
			} catch (Exception e) {
				System.out.println("Error " + e);
			}
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return template;
	}

	// 初使化时把属性配置及类实例化到内存缓存中
	protected Properties loadConfiguration(ServletConfig config)
			throws IOException, FileNotFoundException {
		if (logger.isInfoEnabled()) {
			logger.info("[init] speedframework 'start scanner the actions'");
		}
		// 取得配置文件中的Action的包路径
		String basePackagePath = config.getInitParameter("basePackage");
		if (!StringUtils.isEmpty(basePackagePath)) {
			BASEPACKAGE = basePackagePath;
		}
		String suffix = config.getInitParameter("suffix");
		if (!StringUtils.isEmpty(suffix)) {
			ACTION_SUFFIX = suffix;
		}
		String moduleClassName = config.getInitParameter("moduleClassName");
		if (!StringUtils.isEmpty(moduleClassName)) {
			MODULE_CLASSNAME = moduleClassName;
		}
		// guice context的初使化
		context = SpeedApplicationContext.getInstance();
		// module的初使化
		List<Module> modules = context.getModules();
		if (!StringUtils.isEmpty(MODULE_CLASSNAME)) {
			try {
				SpeedApplicationModule speedModule = (SpeedApplicationModule) Class
						.forName(MODULE_CLASSNAME).newInstance();
				speedModule.registerModules(modules);
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
		context.init(modules);

		// 扫描Action包，把扫描到的URL与实例化的类及方法放到内存缓存中去
		Set<Class<BaseAction>> set = SpeedScanner.scanClasses(BASEPACKAGE);
		if (!set.isEmpty()) {
			initActions(set);
		}

		/*
		 * 得到属性配置文件并load它
		 */
		String propsFile = config.getInitParameter(INIT_PROPS_KEY);
		Properties p = initVelocityProp(propsFile);
		return p;
	}

	// 对模板配置文件进行配置
	@SuppressWarnings("static-access")
	public Properties initVelocityProp(String file) throws IOException,
			FileNotFoundException {
		Properties p = new Properties();
		if (file != null) {
			String realPath = getServletContext().getRealPath(file);
			if (realPath != null) {
				file = realPath;
			}
			p.load(new FileInputStream(file));
		}
		/*
		 * 设置velocity日志文件在web应用中的位置
		 */
		String log = p.getProperty(Velocity.RUNTIME_LOG);
		if (log != null) {
			log = getServletContext().getRealPath(log);
			if (log != null) {
				p.setProperty(Velocity.RUNTIME_LOG, log);
			}
		}
		/*
		 * 设置模板文件在web应用中的位置
		 */
		String path = p.getProperty(Velocity.FILE_RESOURCE_LOADER_PATH);
		if (path != null) {
			path = getServletContext().getRealPath(path);
			if (path != null) {
				p.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, path);
			}
		}
		p.setProperty(Velocity.ENCODING_DEFAULT, "utf-8");
		p.setProperty(Velocity.INPUT_ENCODING, "utf-8");
		p.setProperty(Velocity.OUTPUT_ENCODING, "utf-8");
		return p;
	}

	// 对Action层的类初使化并实例化
	public void initActions(Set<Class<BaseAction>> set) {
		try {
			for (Class<BaseAction> clzz : set) {
				ActionBean bean = new ActionBean();
				bean.setAction(clzz.newInstance());
				Method[] methods = clzz.getDeclaredMethods();
				ConcurrentMap<String, Method> map = new ConcurrentHashMap<String, Method>();
				String className = "";
				Path path = clzz.getAnnotation(Path.class);
				if (null != path && path.value().length > 0) {
					String value = path.value()[0].trim();
					className = value.replace("/", "");
				} else {
					className = CharHelper.lowerFirstWords(clzz.getSimpleName()
							.replace(ACTION_STRING, ""));
				}
				logger.info(className + "=>" + bean);
				for (Method method : methods) {
					String name = "";
					path = method.getAnnotation(Path.class);
					if (null != path && path.value().length > 0) {
						String value = path.value()[0].trim();
						name = value.substring(1,
								value.indexOf('.' + ACTION_SUFFIX));
					} else {
						name = method.getName();
					}
					logger.info(className + "=>" + name + "=>" + method);
					map.put(name, method);
				}
				bean.setMethodMap(map);
				BeanFactoryHashMap.put(className, bean);
			}
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}

	public void destroy() {
		// 清理用过的资源
		super.destroy();
	}

	private static final long serialVersionUID = -7660156137563447424L;
	private static final String ACTION_STRING = "Action";
	private static String BASEPACKAGE = "action";
	private static String ACTION_SUFFIX = "do";
	private static Logger logger = Logger.getLogger(SpeedController.class);
	private static ConcurrentMap<String, ActionBean> BeanFactoryHashMap = new ConcurrentHashMap<String, ActionBean>();
	private static SpeedContext context = null;
	private static String MODULE_CLASSNAME = "";

}
